<?php
namespace Cmf\Library\Acl;

use Phalcon\Mvc\User\Component;
use Phalcon\Acl\Adapter\Memory as AclMemory;
use Phalcon\Acl\Role as AclRole;
use Phalcon\Acl\Resource as AclResource;
use Phalcon\Config;

use Cmf\Models\Menu;
use Cmf\Models\Role;

class Acl extends Component
{
	private $acl;

	private $filePath = '/cache/acl.txt';

	private $publicResources = [
		'session' => ['login','signin','signout'],
		'errors'  => ['show404','show500','show401'],
	];

	public function isAllowed($role,$controller,$action)
	{
		return $this->getAcl()->isAllowed($role,$controller,$action);
	}

	public function getAcl()
	{
		// Check if the ACL is already created
		if(is_object($this->acl)) {
			return $this->acl;
		}

		// Check if the ACL is in APC
		if(function_exists('apc_fetch')) {
			$acl = apc_fetch('cmf_acl');
			if(is_object($acl)) {
				$this->acl = $acl;

				return $acl;
			}
		}

		// Check if the ACL is already generated
		if(!file_exists(APP_DIR.$this->filePath)) {
			$this->acl = $this->rebuild();

			return $this->acl;
		}

		// Get the ACL from the data file
		$data = file_get_contents(APP_DIR.$this->filePath);
		$this->acl = unserialize($data);

		// Store the ACL in APC
		if(function_exists('apc_store')) {
			apc_store('cmf_acl',$this->acl);
		}

		return $this->acl;
	}

	public function rebuild()
	{
		$acl = new AclMemory();

		$acl->setDefaultAction(\Phalcon\Acl::DENY);

		$menus = Menu::find();
		$roles = Role::find('status = 1');

		$acl->addRole(new AclRole('guest'));
		foreach($roles as $role) {
			$acl->addRole(new AclRole($role->title));
		}

		foreach($menus as $menu) {
			$node = explode('/',$menu->url);
			$acl->addResource(new AclResource($node[0]),$node[1]);
		}

		foreach($this->publicResources as $controller => $action) {
			$acl->addResource(new AclResource($controller),$action);
		}

		foreach($roles as $role) {

			foreach($role->getPermissions() as $permission) {
				$acl->allow($role->title,$permission->controller,$permission->action);
			}

			foreach($this->publicResources as $controller => $action) {
				$acl->allow($role->title,$controller,$action);
				$acl->allow('guest',$controller,$action);
			}
		}
		if(touch(APP_DIR.$this->filePath) && is_writable(APP_DIR.$this->filePath)) {

			file_put_contents(APP_DIR.$this->filePath,serialize($acl));

			// Store the ACL in APC
			if(function_exists('apc_store')) {
				apc_store('cmf_acl',$acl);
			}
		} else {
			$this->flash->error(
				'The user does not have write permissions to create the ACL list at '.APP_DIR.$this->filePath
			);
		}

		return $acl;
	}
}